package com.peas.platform.module.icims.expenditure.expenses.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.peas.platform.common.core.enums.BaseEnum;
import com.peas.platform.framework.mybatisplus.domain.BaseDomain;
import com.peas.platform.framework.mybatisplus.po.BasePo;
import com.peas.platform.framework.mybatisplus.service.BaseServiceImpl;
import com.peas.platform.module.icims.base.approve.component.ApproveCallback;
import com.peas.platform.module.icims.base.approve.dto.ApproveDto;
import com.peas.platform.module.icims.base.approve.dto.ApproveOrderNodeDto;
import com.peas.platform.module.icims.base.approve.enums.ApproveOrderBusinessType;
import com.peas.platform.module.icims.base.approve.service.ApproveOrderService;
import com.peas.platform.module.icims.base.approve.service.ApproveService;
import com.peas.platform.module.icims.expenditure.expenses.api.req.SaveExpenditureApplicationReq;
import com.peas.platform.module.icims.expenditure.expenses.api.req.SubmitExpenditureApplicationReq;
import com.peas.platform.module.icims.expenditure.expenses.controller.vo.ExpenditureApplicationFileVo;
import com.peas.platform.module.icims.expenditure.expenses.domain.ExpenditureApplicationDomain;
import com.peas.platform.module.icims.expenditure.expenses.dto.ExpenditureApplicationDto;
import com.peas.platform.module.icims.expenditure.expenses.dto.ExpenditureApplicationFileDto;
import com.peas.platform.module.icims.expenditure.expenses.dto.ExpenditureReimbursementDto;
import com.peas.platform.module.icims.expenditure.expenses.enums.ExpenditureApplicationState;
import com.peas.platform.module.icims.expenditure.expenses.enums.ExpenditureReimbursementState;
import com.peas.platform.module.icims.expenditure.expenses.service.ExpenditureApplicationFileService;
import com.peas.platform.module.icims.expenditure.expenses.service.ExpenditureApplicationService;
import com.peas.platform.module.icims.expenditure.expenses.service.ExpenditureReimbursementService;
import com.peas.platform.module.icims.expenditure.kpis.service.ExpenditureKpiService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

@Service
@Slf4j
public class ExpenditureApplicationServiceImpl extends BaseServiceImpl<ExpenditureApplicationDto> implements ExpenditureApplicationService, ApproveCallback {

    @Resource
    private ExpenditureApplicationDomain expenditureApplicationDomain;
    @Resource
    private ExpenditureApplicationFileService expenditureApplicationFileService;
    @Resource
    private ApproveService approveService;
    @Resource
    private ApproveOrderService approveOrderService;
    @Resource
    private ExpenditureKpiService expenditureKpiService;
    @Resource
    private ExpenditureReimbursementService expenditureReimbursementService;

    @Override
    public BaseDomain<ExpenditureApplicationDto, ? extends BasePo> getDomain() {
        return expenditureApplicationDomain;
    }

    @Transactional
    @Override
    public ExpenditureApplicationDto saveExpenditureApplication(SaveExpenditureApplicationReq vo) {
        ExpenditureApplicationDto ekd = BeanUtil.copyProperties(vo, ExpenditureApplicationDto.class);
        if (StrUtil.isBlank(ekd.getState())) {
            ekd.setState(ExpenditureApplicationState.save.getKey());
        }
        // 提交的时候才判断可用金额
        if (StrUtil.equals(ExpenditureReimbursementState.submit.getKey(), ekd.getState())) {
            Long expenditureKpiId = vo.getExpenditureKpiId();
            Assert.isTrue(expenditureKpiService.queryAvailableAmount(expenditureKpiId).compareTo(vo.getAmount()) >= 0, "报账金额不能超过指标单可用金额");
            if (StrUtil.isNotBlank(vo.getExpenditureEconomicId())) {
                Assert.isTrue(expenditureKpiService.queryAvailableAmount(expenditureKpiId, vo.getExpenditureEconomicId(), false).compareTo(BigDecimal.ZERO) >= 0, "报账金额不能超过指标单可用金额");
                Assert.isTrue(expenditureKpiService.queryAvailableAmount(expenditureKpiId, vo.getExpenditureEconomicId(), true).compareTo(vo.getAmount()) >= 0, "报账金额不能超过指标单可用金额");
            }
        }
        int result;
        if (vo.getId() != null) {
            ExpenditureApplicationDto bean = this.getById(vo.getId());
            ExpenditureApplicationState state = BaseEnum.key2Enum(ExpenditureApplicationState.class, bean.getState());
            Assert.isTrue(ExpenditureApplicationState.save == state || ExpenditureApplicationState.revocation == state || ExpenditureApplicationState.reject == state, "事前用款申请单[" + bean.getApplicationCode() + "]的状态为[" + state.getKey() + "-" + state.getValue() + "]，不允许修改");
            result = this.updateById(ekd);
        } else {
            result = this.save(ekd);
        }
        Assert.isTrue(result > 0, "保存失败");
        // files
        List<ExpenditureApplicationFileDto> files = expenditureApplicationFileService.listByField("expenditureApplicationId", ekd.getId());
        expenditureApplicationFileService.removeBatchByList(files);
        if (CollUtil.isNotEmpty(vo.getFiles())) {
            List<ExpenditureApplicationFileDto> ekidList = new ArrayList<>();
            CollUtil.forEach(vo.getFiles(), (CollUtil.Consumer<ExpenditureApplicationFileVo>) (value, index) -> {
                ExpenditureApplicationFileDto dto = BeanUtil.copyProperties(value, ExpenditureApplicationFileDto.class);
                dto.setExpenditureApplicationId(ekd.getId());
                ekidList.add(dto);
            });
            expenditureApplicationFileService.saveBatch(ekidList);
        }
        return ekd;
    }

    @Transactional
    @Override
    public void submitExpenditureApplication(SubmitExpenditureApplicationReq vo) {
        Assert.notNull(vo.getApproveId(), "请选择审批模版");
        SaveExpenditureApplicationReq v = BeanUtil.copyProperties(vo, SaveExpenditureApplicationReq.class);
        v.setState(ExpenditureApplicationState.submit.getKey());
        v.setItems(vo.getItems());
        v.setFiles(vo.getFiles());
        ExpenditureApplicationDto ead = this.saveExpenditureApplication(v);
        ApproveDto ad = approveService.getById(vo.getApproveId());
        approveOrderService.createApproveOrder(ad, BeanUtil.copyToList(vo.getApproveNodes(), ApproveOrderNodeDto.class), ApproveOrderBusinessType.expenditureApplication, ead.getId(), ead.getApplicationCode(), null, this.getClass());
    }

    @Transactional
    @Override
    public void revocationExpenditureApplication(Long id) {
        ExpenditureApplicationDto dto = this.getById(id);
        Assert.notNull(dto, "找不到事前用款申请单");
        dto.setState(ExpenditureApplicationState.revocation.getKey());
        this.updateById(dto);
        approveOrderService.revocationApproveOrder(ApproveOrderBusinessType.expenditureApplication, id);
    }

    @Transactional
    @Override
    public void completeExpenditureApplication(Long id) {
        ExpenditureApplicationDto dto = this.getById(id);
        Assert.notNull(dto, "找不到事前用款申请单");
        Assert.equals(dto.getState(), ExpenditureApplicationState.pass.getKey(), "只有[" + ExpenditureApplicationState.pass.getValue() + "]状态的事前用款申请单才能完结");
        dto.setState(ExpenditureApplicationState.complete.getKey());
        this.updateById(dto);
    }

    @Override
    public BigDecimal getTotalReimbursementAmount(Long expenditureApplicationId) {
        Assert.notNull(expenditureApplicationId, "申请单ID不能为空");
        BigDecimal totalReimbursementAmount;
        AtomicReference<BigDecimal> total = new AtomicReference<>(BigDecimal.ZERO);
        List<ExpenditureReimbursementDto> list = expenditureReimbursementService.listByField("expenditureApplicationId", expenditureApplicationId);
        log.info("ExpenditureReimbursementDto list:" + list);
        CollUtil.forEach(list, (CollUtil.Consumer<ExpenditureReimbursementDto>) (value, index) -> {
            // 报账是减金额
            String[] states = {
                    ExpenditureReimbursementState.submit.getKey(),
                    ExpenditureReimbursementState.pass.getKey(),
                    ExpenditureReimbursementState.complete.getKey(),
                    ExpenditureReimbursementState.paying.getKey(),
                    ExpenditureReimbursementState.confirm.getKey(),
            };
            if (ArrayUtil.contains(states, value.getState())) {
                total.compareAndSet(total.get(), total.get().add(value.getAmount()));
            }
        });
        totalReimbursementAmount = total.get();
        return totalReimbursementAmount;
    }

    @Transactional
    @Override
    public void callbackAfterApprove(Long businessId, boolean isPass, boolean isFinalPass) {
        if (businessId == null)
            return;
        if (isPass) {
            if (isFinalPass) {
                ExpenditureApplicationDto ead = this.getById(businessId);
                ead.setState(ExpenditureApplicationState.pass.getKey());
                this.updateById(ead);
            }
        } else {
            ExpenditureApplicationDto ead = this.getById(businessId);
            ead.setState(ExpenditureApplicationState.reject.getKey());
            this.updateById(ead);
        }
    }
}
